#include "sdk.h"
#include <wx/regex.h>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include "M_Code_colourmanager.h"
#include "M_Code_StyledTextCtrl.h"
#include "editorcolourset.h"
#include "editorlexerloader.h"
#include "filefilters.h"

const int cbHIGHLIGHT_LINE = -98;
const int cbSELECTION = -99;

EditorColourSet::EditorColourSet( const wxString& setName )
  : m_Name( setName ) {
  LoadAvailableSets();
  if( setName.IsEmpty() ) {
    m_Name = COLORSET_DEFAULT;
  } else {
    Load();
  }
}

EditorColourSet::EditorColourSet( const EditorColourSet& other ) {
  m_Name = other.m_Name;
  m_Sets.clear();
  for( OptionSetsMap::const_iterator it = other.m_Sets.begin(); it != other.m_Sets.end(); ++it ) {
    OptionSet& mset = m_Sets[it->first];
    mset.m_Langs = it->second.m_Langs;
    mset.m_Lexers = it->second.m_Lexers;
    for( int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i ) {
      mset.m_Keywords[i] = it->second.m_Keywords[i];
      mset.m_originalKeywords[i] = it->second.m_originalKeywords[i];
    }
    mset.m_FileMasks = it->second.m_FileMasks;
    mset.m_originalFileMasks = it->second.m_originalFileMasks;
    mset.m_SampleCode = it->second.m_SampleCode;
    mset.m_BreakLine = it->second.m_BreakLine;
    mset.m_DebugLine = it->second.m_DebugLine;
    mset.m_ErrorLine = it->second.m_ErrorLine;
    mset.comment = it->second.comment;
    mset.m_CaseSensitive = it->second.m_CaseSensitive;
    const OptionColours& value = it->second.m_Colours;
    for( unsigned int i = 0; i < value.GetCount(); ++i ) {
      AddOption( it->first, value[i] );
    }
  }
}

EditorColourSet::~EditorColourSet() {
  ClearAllOptionColours();
}

void EditorColourSet::ClearAllOptionColours() {
  for( OptionSetsMap::iterator map_it = m_Sets.begin();
       map_it != m_Sets.end(); ++map_it ) {
    for( OptionColours::iterator vec_it = ( *map_it ).second.m_Colours.begin();
         vec_it != ( *map_it ).second.m_Colours.end(); ++vec_it ) {
      delete( *vec_it );
    }
  }
  m_Sets.clear();
}

void EditorColourSet::LoadAvailableSets() {
  if( Manager::IsBatchBuild() ) {
    return;
  }
  wxDir dir;
  wxString filename;
  FileManager *fm = FileManager::Get();
  std::list<LoaderBase*> loaders;
  int count = 0;
  wxString path = ConfigManager::GetFolder( sdDataGlobal ) + _T( "/lexers/" );
  if( wxDirExists( path ) && dir.Open( path ) ) {
    bool ok = dir.GetFirst( &filename, _T( "lexer_*.xml" ), wxDIR_FILES );
    while( ok ) {
      loaders.push_back( fm->Load( path + filename ) );
      ok = dir.GetNext( &filename );
      ++count;
    }
    Manager::Get()->GetLogManager()->Log( F( _T( "扫描到 %d 个文件 检测lexers目录为: %s" ), count, path.wx_str() ) );
  }
  EditorLexerLoader lex( this );
  for( std::list<LoaderBase*>::iterator it = loaders.begin(); it != loaders.end(); ++it ) {
    lex.Load( *it );
  }
  ::Delete( loaders );
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    wxString lang = it->second.m_Langs;
    if( lang.IsEmpty() ) {
      continue;
    }
    for( int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i ) {
      it->second.m_originalKeywords[i] = it->second.m_Keywords[i];
    }
    it->second.m_originalFileMasks = it->second.m_FileMasks;
    unsigned int i = 0;
    while( i < it->second.m_Colours.GetCount() ) {
      OptionColour* opt = it->second.m_Colours.Item( i );
      if( opt->value < 0 &&
          opt->value != cbSELECTION &&
          opt->value != cbHIGHLIGHT_LINE ) {
        it->second.m_Colours.Remove( opt );
        delete opt;
      } else
      { ++i; }
    }
  }
}

HighlightLanguage EditorColourSet::AddHighlightLanguage( int lexer, const wxString& name ) {
  if( lexer <= wxSCI_LEX_NULL || lexer >  wxSCI_LEX_LAST || name.IsEmpty() ) {
    return HL_NONE;
  }
  wxString newID;
  size_t pos = 0;
  while( pos < name.Length() ) {
    wxChar ch = name[pos];
    if( wxIsalnum( ch ) || ch == _T( '_' ) ) {
      newID.Append( ch );
    } else if( wxIsspace( ch ) ) {
      newID.Append( _T( '_' ) );
    }
    ++pos;
  }
  if( wxIsdigit( newID.GetChar( 0 ) ) || newID.GetChar( 0 ) == _T( '_' ) ) {
    newID.Prepend( _T( 'A' ) );
  }
  if( GetHighlightLanguage( newID ) != HL_NONE ) {
    return HL_NONE;
  }
  m_Sets[newID].m_Langs = name;
  m_Sets[newID].m_Lexers = lexer;
  return newID;
}

HighlightLanguage EditorColourSet::GetHighlightLanguage( const wxString& name ) {
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    if( it->second.m_Langs.CmpNoCase( name ) == 0 ) {
      return it->first;
    }
  }
  return HL_NONE;
}


HighlightLanguage EditorColourSet::GetHighlightLanguage( int lexer ) {
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    if( it->second.m_Lexers == lexer ) {
      return it->first;
    }
  }
  return HL_NONE;
}

static int CompareStringNoCase( const wxString& first, const wxString& second ) {
  return first.CmpNoCase( second );
}

wxArrayString EditorColourSet::GetAllHighlightLanguages() {
  wxArrayString ret;
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    if( !it->second.m_Langs.IsEmpty() ) {
      ret.Add( it->second.m_Langs );
    }
  }
  ret.Sort( CompareStringNoCase );
  return ret;
}

void EditorColourSet::UpdateOptionsWithSameName( HighlightLanguage lang, OptionColour* base ) {
  if( !base ) {
    return;
  }
  int idx = -1;
  OptionSet& mset = m_Sets[lang];
  for( unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i ) {
    OptionColour* opt = mset.m_Colours.Item( i );
    if( opt == base ) {
      idx = i;
      break;
    }
  }
  if( idx == -1 ) {
    return;
  }
  for( unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i ) {
    if( ( int )i == idx ) {
      continue;
    }
    OptionColour* opt = mset.m_Colours.Item( i );
    if( !opt->name.Matches( base->name ) ) {
      continue;
    }
    opt->fore = base->fore;
    opt->back = base->back;
    opt->bold = base->bold;
    opt->italics = base->italics;
    opt->underlined = base->underlined;
  }
}

bool EditorColourSet::AddOption( HighlightLanguage lang, OptionColour* option, bool checkIfExists ) {
  if( lang == HL_NONE ) {
    return false;
  }
  if( checkIfExists && GetOptionByValue( lang, option->value ) ) {
    return false;
  }
  OptionColours& colours =  m_Sets[lang].m_Colours;
  colours.Add( new OptionColour( *option ) );
  return true;
}

void EditorColourSet::AddOption( HighlightLanguage lang, const wxString& name, int value, wxColour fore,
                                 wxColour back, bool bold, bool italics, bool underlined, bool isStyle ) {
  if( lang == HL_NONE ) {
    return;
  }
  OptionColour* opt = new OptionColour;
  opt->name = name;
  opt->value = value;
  opt->fore = fore;
  opt->back = back;
  opt->bold = bold;
  opt->italics = italics;
  opt->underlined = underlined;
  opt->isStyle = isStyle;
  opt->originalfore = fore;
  opt->originalback = back;
  opt->originalbold = bold;
  opt->originalitalics = italics;
  opt->originalunderlined = underlined;
  opt->originalisStyle = isStyle;
  AddOption( lang, opt );
  delete opt;
}

OptionColour* EditorColourSet::GetOptionByName( HighlightLanguage lang, const wxString& name ) {
  if( lang == HL_NONE ) {
    return nullptr;
  }
  OptionSet& mset = m_Sets[lang];
  for( unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i ) {
    OptionColour* opt = mset.m_Colours.Item( i );
    if( opt->name == name ) {
      return opt;
    }
  }
  return nullptr;
}

OptionColour* EditorColourSet::GetOptionByValue( HighlightLanguage lang, int value ) {
  if( lang == HL_NONE ) {
    return nullptr;
  }
  OptionSet& mset = m_Sets[lang];
  for( unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i ) {
    OptionColour* opt = mset.m_Colours.Item( i );
    if( opt->value == value ) {
      return opt;
    }
  }
  return nullptr;
}

OptionColour* EditorColourSet::GetOptionByIndex( HighlightLanguage lang, int index ) {
  if( lang == HL_NONE ) {
    return nullptr;
  }
  return m_Sets[lang].m_Colours.Item( index );
}

int EditorColourSet::GetOptionCount( HighlightLanguage lang ) {
  return m_Sets[lang].m_Colours.GetCount();
}


OptionColour* EditorColourSet::GetDefaultOption( HighlightLanguage lang ) {
  if( lang == HL_NONE ) {
    return nullptr;
  }
  OptionSet& mset = m_Sets[lang];
  OptionColour *defaultOpt = nullptr;
  for( size_t i = 0; i < mset.m_Colours.GetCount(); ++i ) {
    OptionColour* opt = mset.m_Colours.Item( i );
    if( opt->name == _T( "Default" ) ) {
      return opt;
    }
    if( opt->value == 0 ) {
      defaultOpt = opt;
    }
  }
  return defaultOpt;
}

HighlightLanguage EditorColourSet::GetLanguageForFilename( const wxString& filename ) {
  wxString lfname = wxFileName( filename.Lower() ).GetFullName();
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    for( unsigned int x = 0; x < it->second.m_FileMasks.GetCount(); ++x ) {
      if( lfname.Matches( it->second.m_FileMasks.Item( x ) ) ) {
        return it->first;
      }
    }
  }
  if( wxFileExists( filename ) ) {
    wxFileInputStream input( filename );
    wxTextInputStream text( input );
    wxString line;
    if( input.IsOk() && !input.Eof() ) {
      line = text.ReadLine();
    }
    if( !line.IsEmpty() ) {
      wxRegEx reSheBang( _T( "#![ \t]*([a-zA-Z/]+)[ \t]*([a-zA-Z/]*)" ) );
      if( reSheBang.Matches( line ) ) {
        wxString prog = reSheBang.GetMatch( line, 1 );
        if( prog.EndsWith( _T( "env" ) ) ) {
          prog = reSheBang.GetMatch( line, 2 );
        }
        if( prog.Find( _T( '/' ) ) != wxNOT_FOUND ) {
          prog = prog.AfterLast( _T( '/' ) );
        }
        if( prog == _T( "sh" ) ) {
          prog = _T( "bash" );
        }
        HighlightLanguage lang = GetHighlightLanguage( prog );
        if( lang !=  HL_NONE ) {
          return lang;
        }
      } else if( line.Trim().StartsWith( _T( "<?xml" ) ) ) {
        return GetHighlightLanguage( _T( "XML" ) );
      }
    }
  }
  const wxString cppNames = _T( "|"
                                "algorithm|"  "atomic|"       "array|"              "bitset|"
                                "chrono|"     "complex|"      "condition_variable|" "deque|"
                                "exception|"  "fstream|"      "forward_list|"       "future|"
                                "functional|" "hash_map|"     "hash_set|"           "initializer_list|"
                                "iomanip|"    "ios|"          "iostream|"           "istream|"
                                "iterator|"   "limits|"       "list|"               "locale|"
                                "map|"        "memory|"       "mutex|"              "new|"
                                "numeric|"    "ostream|"      "queue|"              "random|"
                                "ratio|"      "regex|"        "set|"                "sstream|"
                                "stack|"      "stdexcept|"    "streambuf|"          "string|"
                                "strstream|"  "system_error|" "thread|"             "tuple|"
                                "typeinfo|"   "type_traits|"  "unordered_map|"      "unordered_set|"
                                "utility|"    "valarray|"     "vector|"
                                "cassert|"   "cctype|"  "cerrno|"  "cfenv|"    "cfloat|"
                                "cinttypes|" "ciso646|" "climits|" "clocale|"  "cmath|"
                                "csetjmp|"   "csignal|" "cstdarg|" "cstdbool|" "cstddef|"
                                "cstdint|"   "cstdio|"  "cstdlib|" "cstring|"  "ctgmath|"
                                "ctime|"     "cuchar|"  "cwchar|"  "cwctype|" );
  if( cppNames.Find( _T( "|" ) + lfname + _T( "|" ) ) != wxNOT_FOUND ) {
    return GetHighlightLanguage( _T( "C/C++" ) );
  }
  return HL_NONE;
}

wxString EditorColourSet::GetLanguageName( HighlightLanguage lang ) {
  if( lang == HL_NONE ) {
    return _T( "Plain text" );
  }
  wxString name = m_Sets[lang].m_Langs;
  if( !name.IsEmpty() ) {
    return name;
  }
  return _T( "Plain text" );
}

void EditorColourSet::DoApplyStyle( M_Code_StyledTextCtrl* control, int value, OptionColour* option ) {
  if( option->fore != wxNullColour ) {
    control->StyleSetForeground( value, option->fore );
  }
  if( option->back != wxNullColour ) {
    control->StyleSetBackground( value, option->back );
  }
  control->StyleSetBold( value, option->bold );
  control->StyleSetItalic( value, option->italics );
  control->StyleSetUnderline( value, option->underlined );
}

HighlightLanguage EditorColourSet::Apply( M_Code_Editor* editor, HighlightLanguage lang ) {
  if( !editor ) {
    return HL_NONE;
  }
  if( lang == HL_AUTO ) {
    lang = GetLanguageForFilename( editor->GetFilename() );
  }
  const bool isC = ( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "no_stl_in_c" ), true )
                     && lang == GetHighlightLanguage( _T( "C/C++" ) )
                     && editor->GetFilename().Lower().EndsWith( _T( ".c" ) ) );
  Apply( lang, editor->GetLeftSplitViewControl(),  isC );
  Apply( lang, editor->GetRightSplitViewControl(), isC );
  return lang;
}


void EditorColourSet::Apply( HighlightLanguage lang, M_Code_StyledTextCtrl* control, bool isC ) {
  if( !control ) {
    return;
  }
  control->StyleClearAll();
  if( lang == HL_NONE ) {
    return;
  }
  OptionColour* defaults = GetDefaultOption( lang );
  OptionSet& mset = m_Sets[lang];
  control->SetLexer( mset.m_Lexers );
  control->SetStyleBits( control->GetStyleBitsNeeded() );
  if( defaults ) {
    int countStyles = 1 << control->GetStyleBits();
    for( int i = 0; i <= countStyles; ++i ) {
      if( i < 33 || ( i > 39 && i < wxSCI_STYLE_MAX ) ) {
        DoApplyStyle( control, i, defaults );
      }
    }
  }
  ColourManager *colours = Manager::Get()->GetColourManager();
  control->StyleSetForeground( wxSCI_STYLE_LINENUMBER, colours->GetColour( _T( "editor_linenumbers_fg" ) ) );
  control->StyleSetBackground( wxSCI_STYLE_LINENUMBER, colours->GetColour( _T( "editor_linenumbers_bg" ) ) );
  for( unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i ) {
    OptionColour* opt = mset.m_Colours.Item( i );
    if( opt->isStyle ) {
      DoApplyStyle( control, opt->value, opt );
    } else {
      if( opt->value == cbHIGHLIGHT_LINE ) {
        control->SetCaretLineBackground( opt->back );
        Manager::Get()->GetConfigManager( _T( "editor" ) )->Write( _T( "/highlight_caret_line_colour" ), opt->back );
      } else if( opt->value == cbSELECTION ) {
        if( opt->back != wxNullColour ) {
          control->SetSelBackground( true, opt->back );
        } else
        { control->SetSelBackground( false, wxColour( 0xC0, 0xC0, 0xC0 ) ); }
        if( opt->fore != wxNullColour ) {
          control->SetSelForeground( true, opt->fore );
        } else
        { control->SetSelForeground( false, *wxBLACK ); }
      }
    }
  }
  for( int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i ) {
    if( !isC || i != 1 ) {
      control->SetKeyWords( i, mset.m_Keywords[i] );
    }
  }
  control->Colourise( 0, -1 );
}

void EditorColourSet::Save() {
  if( Manager::IsBatchBuild() ) {
    return;
  }
  wxString key;
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "editor" ) );
  cfg->DeleteSubPath( _T( "/colour_sets/" ) + m_Name );
  cfg->Write( _T( "/colour_sets/" ) + m_Name + _T( "/name" ), m_Name );
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    if( it->first == HL_NONE || it->first == HL_AUTO ) {
      continue;
    }
    wxString lang = it->first;
    bool gsaved = false;
    key.Clear();
    key << _T( "/colour_sets/" ) << m_Name << _T( '/' ) << lang;
    for( unsigned int i = 0; i < it->second.m_Colours.GetCount(); ++i ) {
      OptionColour* opt = it->second.m_Colours.Item( i );
      wxString tmpKey;
      tmpKey << key << _T( "/style" ) << F( _T( "%u" ), i );
      bool saved = false;
      if( opt->fore != opt->originalfore ) {
        cfg->Write( tmpKey + _T( "/fore" ), opt->fore );
        saved = true;
      }
      if( opt->back != opt->originalback ) {
        cfg->Write( tmpKey + _T( "/back" ), opt->back );
        saved = true;
      }
      if( opt->bold != opt->originalbold ) {
        cfg->Write( tmpKey + _T( "/bold" ), opt->bold );
        saved = true;
      }
      if( opt->italics != opt->originalitalics ) {
        cfg->Write( tmpKey + _T( "/italics" ), opt->italics );
        saved = true;
      }
      if( opt->underlined != opt->originalunderlined ) {
        cfg->Write( tmpKey + _T( "/underlined" ), opt->underlined );
        saved = true;
      }
      if( opt->isStyle != opt->originalisStyle ) {
        cfg->Write( tmpKey + _T( "/isStyle" ), opt->isStyle );
        saved = true;
      }
      if( saved ) {
        cfg->Write( tmpKey + _T( "/name" ), opt->name, true );
        gsaved = true;
      }
    }
    wxString tmpkey;
    for( int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i ) {
      if( it->second.m_Keywords[i] != it->second.m_originalKeywords[i] ) {
        tmpkey.Printf( _T( "%s/editor/keywords/set%d" ), key.c_str(), i );
        cfg->Write( tmpkey, it->second.m_Keywords[i] );
        gsaved = true;
      }
    }
    tmpkey.Printf( _T( "%s/editor/filemasks" ), key.c_str() );
    wxString tmparr = GetStringFromArray( it->second.m_FileMasks, _T( "," ) );
    wxString tmparrorig = GetStringFromArray( it->second.m_originalFileMasks, _T( "," ) );
    if( tmparr != tmparrorig ) {
      cfg->Write( tmpkey, tmparr );
      gsaved = true;
    }
    if( gsaved ) {
      cfg->Write( key + _T( "/name" ), it->second.m_Langs );
    }
  }
}

void EditorColourSet::Load() {
  if( Manager::IsBatchBuild() ) {
    return;
  }
  static bool s_notifiedUser = false;
  wxString key;
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "editor" ) );
  m_Name = cfg->Read( _T( "/colour_sets/" ) + m_Name + _T( "/name" ), m_Name );
  int x = 0;
  for( OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it ) {
    if( it->first == HL_NONE || it->first == HL_AUTO ) {
      continue;
    }
    key.Clear();
    key << _T( "/colour_sets/" ) << m_Name << _T( "/set" ) << F( _T( "%d" ), x++ );
    if( cfg->Exists( key + _T( "/name" ) ) ) {
      cfg->DeleteSubPath( key );
      if( !s_notifiedUser ) {
        msging( _T( "编辑器语法突出显示配置的保存方式已更改.\n"
                    "所有支持的语言的语法突出显示现在将恢复为默认值.\n"
                    "We 're sorry for the inconvenience..." ),
                _T( "Information" ),
                wxICON_INFORMATION );
        s_notifiedUser = true;
      }
      continue;
    }
    cfg->DeleteSubPath( key );
    key.Clear();
    key << _T( "/colour_sets/" ) << m_Name << _T( '/' ) << it->first;
    if( !cfg->Exists( key + _T( "/name" ) ) ) {
      cfg->DeleteSubPath( key );
      continue;
    }
    for( unsigned int i = 0; i < it->second.m_Colours.GetCount(); ++i ) {
      wxString tmpKey;
      tmpKey << key << _T( "/style" ) << F( _T( "%u" ), i );
      if( !cfg->Exists( tmpKey + _T( "/name" ) ) ) {
        cfg->DeleteSubPath( tmpKey );
        continue;
      }
      wxString name = cfg->Read( tmpKey + _T( "/name" ) );
      for( size_t j = 0; j < it->second.m_Colours.GetCount(); ++j ) {
        OptionColour* opt = it->second.m_Colours.Item( j );
        if( !opt || opt->name != name ) {
          continue;
        }
        if( cfg->Exists( tmpKey + _T( "/fore" ) ) ) {
          opt->fore = cfg->ReadColour( tmpKey + _T( "/fore" ), opt->fore );
        }
        if( cfg->Exists( tmpKey + _T( "/back" ) ) ) {
          opt->back = cfg->ReadColour( tmpKey + _T( "/back" ), opt->back );
        }
        if( cfg->Exists( tmpKey + _T( "/bold" ) ) ) {
          opt->bold = cfg->ReadBool( tmpKey + _T( "/bold" ), opt->bold );
        }
        if( cfg->Exists( tmpKey + _T( "/italics" ) ) ) {
          opt->italics = cfg->ReadBool( tmpKey + _T( "/italics" ), opt->italics );
        }
        if( cfg->Exists( tmpKey + _T( "/underlined" ) ) ) {
          opt->underlined = cfg->ReadBool( tmpKey + _T( "/underlined" ), opt->underlined );
        }
        if( cfg->Exists( tmpKey + _T( "/isStyle" ) ) ) {
          opt->isStyle = cfg->ReadBool( tmpKey + _T( "/isStyle" ),    opt->isStyle );
        }
      }
    }
    wxString tmpkey;
    for( int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i ) {
      tmpkey.Printf( _T( "%s/editor/keywords/set%d" ), key.c_str(), i );
      if( cfg->Exists( tmpkey ) ) {
        it->second.m_Keywords[i] = cfg->Read( tmpkey, wxEmptyString );
      }
    }
    tmpkey.Printf( _T( "%s/editor/filemasks" ), key.c_str() );
    if( cfg->Exists( tmpkey ) ) {
      it->second.m_FileMasks = GetArrayFromString( cfg->Read( tmpkey, wxEmptyString ), _T( "," ) );
    }
  }
}

void EditorColourSet::Reset( HighlightLanguage lang ) {
  if( Manager::IsBatchBuild() ) {
    return;
  }
  wxString key;
  key << _T( "/colour_sets/" ) << m_Name << _T( '/' ) << lang;
  if( Manager::Get()->GetConfigManager( _T( "editor" ) )->Exists( key + _T( "/name" ) ) ) {
    Manager::Get()->GetConfigManager( _T( "editor" ) )->DeleteSubPath( key );
  }
  ClearAllOptionColours();
  LoadAvailableSets();
  Load();
}

wxString& EditorColourSet::GetKeywords( HighlightLanguage lang, int idx ) {
  if( idx < 0 || idx > wxSCI_KEYWORDSET_MAX ) {
    idx = 0;
  }
  return m_Sets[lang].m_Keywords[idx];
}

void EditorColourSet::SetKeywords( HighlightLanguage lang, int idx, const wxString& keywords ) {
  if( lang != HL_NONE && idx >= 0 && idx <= wxSCI_KEYWORDSET_MAX ) {
    wxString tmp( _T( ' ' ), keywords.length() );
    const wxChar *src = keywords.c_str();
    wxChar *dst = ( wxChar * ) tmp.c_str();
    wxChar c;
    size_t len = 0;
    while( ( c = *src ) ) {
      ++src;
      if( c > _T( ' ' ) ) {
        *dst = c;
      } else {
        *dst = _T( ' ' );
        while( *src && *src < _T( ' ' ) ) {
          ++src;
        }
      }
      ++dst;
      ++len;
    }
    tmp.Truncate( len );
    OptionSet& mset = m_Sets[lang];
    mset.m_Keywords[idx] = tmp;
  }
}

const wxArrayString& EditorColourSet::GetFileMasks( HighlightLanguage lang ) {
  return m_Sets[lang].m_FileMasks;
}

void EditorColourSet::SetFileMasks( HighlightLanguage lang, const wxString& masks, const wxString& separator ) {
  if( lang != HL_NONE ) {
    m_Sets[lang].m_FileMasks = GetArrayFromString( masks.Lower(), separator );
    FileFilters::Add( F( _T( "%s 文件" ), m_Sets[lang].m_Langs.c_str() ), masks );
  }
}

wxString EditorColourSet::GetSampleCode( HighlightLanguage lang, int* breakLine, int* debugLine, int* errorLine ) {
  if( lang == HL_NONE ) {
    return wxEmptyString;
  }
  OptionSet& mset = m_Sets[lang];
  if( breakLine ) {
    *breakLine = mset.m_BreakLine;
  }
  if( debugLine ) {
    *debugLine = mset.m_DebugLine;
  }
  if( errorLine ) {
    *errorLine = mset.m_ErrorLine;
  }
  wxString shortname;
  if( mset.m_SampleCode.IsEmpty() ) {
    shortname = _T( "lexer_" ) + lang + _T( ".sample" );
  } else {
    shortname = mset.m_SampleCode;
  }
  wxString path = ConfigManager::GetFolder( sdDataGlobal ) + _T( "/lexers/" );
  if( wxFileExists( path + shortname ) ) {
    return path + shortname;
  }
  return wxEmptyString;
}

void EditorColourSet::SetSampleCode( HighlightLanguage lang, const wxString& sample, int breakLine, int debugLine, int errorLine ) {
  if( lang == HL_NONE ) {
    return;
  }
  OptionSet& mset = m_Sets[lang];
  mset.m_SampleCode = sample;
  mset.m_BreakLine = breakLine;
  mset.m_DebugLine = debugLine;
  mset.m_ErrorLine = errorLine;
}

void EditorColourSet::SetCommentToken( HighlightLanguage lang, CommentToken token ) {
  if( lang == HL_NONE ) {
    return;
  }
  m_Sets[lang].comment = token;
}

CommentToken EditorColourSet::GetCommentToken( HighlightLanguage lang ) {
  CommentToken com;
  com.lineComment = _T( "" );
  com.doxygenLineComment = _T( "" );
  com.streamCommentStart = _T( "" );
  com.streamCommentEnd = _T( "" );
  com.doxygenStreamCommentStart = _T( "" );
  com.doxygenStreamCommentEnd = _T( "" );
  com.boxCommentStart = _T( "" );
  com.boxCommentMid = _T( "" );
  com.boxCommentEnd = _T( "" );
  if( lang != HL_NONE ) {
    com = m_Sets[lang].comment;
  }
  return com;
}

void EditorColourSet::SetCaseSensitivity( HighlightLanguage lang, bool CaseSensitive ) {
  if( lang == HL_NONE ) {
    return;
  }
  m_Sets[lang].m_CaseSensitive = CaseSensitive;
}

bool EditorColourSet::GetCaseSensitivity( HighlightLanguage lang ) {
  if( lang == HL_NONE ) {
    return false;
  }
  return m_Sets[lang].m_CaseSensitive;
}

void EditorColourSet::SetStringLexerStyles( HighlightLanguage lang, const std::set<int> &styles ) {
  if( lang == HL_NONE ) {
    return;
  }
  M_Code_StyledTextCtrl::GetStringLexerStyles()[m_Sets[lang].m_Lexers] = styles;
}

void EditorColourSet::SetCommentLexerStyles( HighlightLanguage lang, const std::set<int> &styles ) {
  if( lang == HL_NONE ) {
    return;
  }
  M_Code_StyledTextCtrl::GetCommentLexerStyles()[m_Sets[lang].m_Lexers] = styles;
}

void EditorColourSet::SetCharacterLexerStyles( HighlightLanguage lang, const std::set<int> &styles ) {
  if( lang == HL_NONE ) {
    return;
  }
  M_Code_StyledTextCtrl::GetCharacterLexerStyles()[m_Sets[lang].m_Lexers] = styles;
}

void EditorColourSet::SetPreprocessorLexerStyles( HighlightLanguage lang, const std::set<int> &styles ) {
  if( lang == HL_NONE ) {
    return;
  }
  M_Code_StyledTextCtrl::GetPreprocessorLexerStyles()[m_Sets[lang].m_Lexers] = styles;
}
